//! system管理各用户的信息
use std::sync::Arc;

use ane_system::{
    dao::{User, UserSimpletVo},
    entity::{sys_dept, sys_user},
};
use axum::{
    extract::{Path, Query},
    http::StatusCode,
    response::IntoResponse,
    routing::{get, post},
    Extension, Router,
};
use common::page::{PageInfo, PageParams};
use gen_table::model::Order;
use loginmanager::AuthUser;
use sea_orm::{ColumnTrait, Condition, ConnectionTrait, QueryFilter};
use sea_orm::{EntityTrait, TransactionTrait};
use serde::Deserialize;
use validator::Validate;

use crate::{
    error::{AppError, AppResult},
    extract::{Json, ValJson},
    security::{LoginUser, Security},
    AppState,
};

use super::SysFilter;

/// [User](ane_system::entity::sys_user)操作相关的路由
/// - `{prefix}/` `GET|POST` [User](ane_system::entity::sys_user)的查询和创建
/// - `{prefix}/:id` `GET|PUT|DELETE` [User](ane_system::entity::sys_user)的更新和删除
/// - `{prefix}/assign_role` `POST` 分配角色
/// - `{prefix}/update_password` `POST` 直接修改用户的密码
pub fn get_route<S>(prefix: &str) -> Router<S>
where
    S: Sync + Send + Clone + 'static,
{
    let f = move |p: &str| -> String { format!("{}{p}", prefix) };
    Router::<S>::new()
        .route(&f("/"), get(list).post(create))
        .route(&f("/:id"), get(fetch_one).put(update).delete(delete))
        .route(&f("/assign_role"), post(assign_role))
        .route(&f("/update_password"), post(update_password))
}

pub async fn fetch_one(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
    user: LoginUser,
) -> AppResult<impl IntoResponse> {
    if id != user.id && user.data_scope() == 5 {
        return Err(AppError::Forbidden);
    }
    let cond = user.dept_cond();
    User::get_detail_by_id(state.db(), id, None, Some(cond), false)
        .await
        .map(Json)
        .map_err(Into::into)
}

pub async fn list(
    Extension(state): Extension<AppState>,
    Query(filter): Query<SysFilter>,
    user: LoginUser,
) -> AppResult<impl IntoResponse> {
    let order = filter
        .order
        .map(|f| Order::parse_to_map(&f))
        .map(|f| f.get("created_at").map(Order::is_asc))
        .flatten();
    let mut cond = user.dept_cond();
    if user.data_scope() == 5 {
        cond = cond.add(sys_user::Column::UserId.eq(user.id));
    }
    let cond = if let Some(id) = filter.dept_id {
        Condition::all().add(cond).add(
            Condition::any()
                .add(sys_dept::Column::DeptId.eq(id))
                .add(sys_dept::Column::Ancestors.contains(format!("/{}/", id))),
        )
    } else {
        cond
    };
    User::list::<UserSimpletVo>(
        state.db(),
        filter.page.zero_page(),
        filter.page.page_size(),
        None,
        Some(cond),
        filter.username,
        filter.nickname,
        filter.status,
        order,
    )
    .await
    .map(|(datas, total)| PageInfo::new(datas, &filter.page, total))
    .map(Json)
    .map_err(Into::into)
}

#[derive(Debug, Deserialize, Validate)]
pub struct NewUserDo {
    #[validate(nested)]
    #[serde(flatten)]
    user_data: ane_system::dao::NewUserDo,
    posts: Option<Vec<i32>>,
    roles: Option<Vec<i32>>,
}

pub async fn create(
    Extension(state): Extension<AppState>,
    user: LoginUser,
    ValJson(data): ValJson<NewUserDo>,
) -> AppResult<impl IntoResponse> {
    let txn = state.db().begin().await?;
    let roles = data.roles;
    let posts = data.posts;
    let data = data.user_data;
    // 检查用户名
    if User::check_name(&txn, &data.username).await? {
        Err(AppError::BadParam(format!(
            "username `{}` exist",
            data.username
        )))?;
    }
    // 检查单位
    if !user.can_access_dept_id(&txn, data.dept_id).await? {
        Err(AppError::Forbidden)?;
    }

    let res = User::create(&txn, data).await?;
    if let Some(roles) = roles {
        User::update_roles(&txn, res.user_id, roles).await?
    }
    if let Some(posts) = posts {
        User::update_posts(&txn, res.user_id, posts).await?
    }
    txn.commit().await?;
    Ok(Json(res).into_response())
}

#[derive(Debug, Deserialize, Validate)]
pub struct UpdateUserDo {
    #[validate(nested)]
    #[serde(flatten)]
    user_data: ane_system::dao::UpdateUserDo,
    roles: Option<Vec<i32>>,
    posts: Option<Vec<i32>>,
}

pub async fn update(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
    user: LoginUser,
    ValJson(mut data): ValJson<UpdateUserDo>,
) -> AppResult<impl IntoResponse> {
    let roles = data.roles;
    let posts = data.posts;
    let mut data = data.user_data;
    if id != user.id && user.data_scope() == 5 {
        Err(AppError::Forbidden)?;
    }
    let txn = state.db().begin().await?;
    // 检查是否可以访问用户
    let model = sys_user::Entity::find_by_id(id)
        .left_join(sys_dept::Entity)
        .filter(sys_user::Column::DelFlag.eq(false))
        .filter(user.dept_cond())
        .one(&txn)
        .await?
        .ok_or(AppError::NotFount)?;
    // 检查用户名
    if let Some(username) = &data.username {
        if &model.username != username && User::check_name(&txn, username).await? {
            Err(AppError::BadParam(format!("username `{}` exist", username)))?;
        }
    };
    // 检查单位
    if matches!(data.dept_id, Some(dept_id) if !user.can_access_dept_id(&txn, dept_id).await? ) {
        Err(AppError::Forbidden)?;
    }

    // 禁止修改 admin 用户的status
    if model.username == "admin" || id == user.id {
        data.status = None;
        data.dept_id = None;
        data.username = None;
    }
    let res = User::update(&txn, id, data)
        .await
        .map(Json)
        .map_err(Into::into);
    if let Some(roles) = roles {
        User::update_roles(&txn, id, roles).await?
    }
    if let Some(posts) = posts {
        User::update_posts(&txn, id, posts).await?
    }
    txn.commit().await?;
    // 更新就清除缓存
    Security::clean_users_cache(&state, vec![id]).await;
    res
}

pub async fn delete(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
    user: LoginUser,
) -> AppResult<impl IntoResponse> {
    /// 禁止删除自己
    if user.id == id {
        Err(AppError::Forbidden)?
    }

    // 检查是否可以访问用户
    let model = sys_user::Entity::find_by_id(id)
        .left_join(sys_dept::Entity)
        .filter(sys_user::Column::DelFlag.eq(false))
        .filter(user.dept_cond())
        .one(state.db())
        .await?
        .ok_or(AppError::NotFount)?;

    // 禁止删除 admin 用户
    if model.username == "admin" {
        Err(AppError::Forbidden)?
    }

    let res = User::soft_delete(state.db(), id)
        .await
        .map_err(Into::into)
        .map(|_| "Ok");
    // 更新就清除缓存
    Security::clean_users_cache(&state, vec![id]).await;
    res
}

#[derive(Deserialize, Validate)]
pub struct AssignRoledDo {
    user_id: i32,
    role_ids: Vec<i32>,
}

/// 分配角色
pub async fn assign_role(
    Extension(state): Extension<AppState>,
    user: LoginUser,
    ValJson(data): ValJson<AssignRoledDo>,
) -> AppResult<impl IntoResponse> {
    // 检查是否可以访问用户
    if !user.can_access_user_id(state.db(), data.user_id).await? {
        Err(AppError::Forbidden)?
    };
    User::update_roles(state.db(), data.user_id, data.role_ids).await?;
    Ok("Ok".to_owned())
}

#[derive(Deserialize, Validate)]
pub struct ChangePasswordDo {
    id: i32,
    #[validate(length(min = 3, max = 32), non_control_character)]
    new_pwd: String,
}

/// 修改用户的密码
pub async fn update_password(
    Extension(state): Extension<AppState>,
    user: LoginUser,
    ValJson(data): ValJson<ChangePasswordDo>,
) -> AppResult<impl IntoResponse> {
    // 检查是否可以访问用户
    if !user.can_access_user_id(state.db(), data.id).await? {
        Err(AppError::Forbidden)?
    };
    User::change_password(state.db(), data.id, data.new_pwd).await?;
    Ok("Ok".to_owned())
}
